using System;
using System.ComponentModel;
using System.Text;
using System.Collections.Generic;
using System.Globalization;

namespace BlogEngine.Core
{
	/// <summary>
	/// This is the base class from which most business objects will be derived. 
	/// To create a business object, inherit from this class.
	/// </summary>
	/// <typeparam name="TYPE">The type of the derived class.</typeparam>
	[Serializable, DataObject]
	public abstract class Postable<TYPE> : BusinessBase<TYPE, int>, IPostable, IPublishable
		where TYPE : Postable<TYPE>, new()
	{
		private string _author;
		private Uri _permaLink;
		private DateTime? _datePublished;
		private string _title;
		private string _description;
		private string _content;
		private IPostable _parent;
		private PostType _type;
		private PostStatusType _status;
		private PostCommentStatusType _commentStatus;
		private PostPingStatusType _pingStatus;
		private string _mimeType;
		private string _slug;
		private IDictionary<string, string> _meta;
		private StateCollection<Term> _terms;

		/// <summary>
		/// Initializes a new instance of the <see cref="Postable&lt;TYPE&gt;"/> class.
		/// </summary>
		public Postable()
		{
			_terms = new StateCollection<Term>();
			_meta = new Dictionary<string, string>();
		}

		#region Events

		/// <summary>
		/// Occurs when the page is being served to the output stream.
		/// </summary>
		public static event EventHandler<ServingEventArgs> Serving;
		/// <summary>
		/// Raises the event in a safe way
		/// </summary>
		public static void OnServing(TYPE post, ServingEventArgs arg)
		{
			if (Serving != null)
				Serving(post, arg);
		}

		/// <summary>
		/// Raises the Serving event
		/// </summary>
		public void OnServing(ServingEventArgs arg)
		{
			if (Serving != null)
				Serving(this, arg);
		}

		#endregion

		#region Properties

		/// <summary>
		/// Gets the keywords.
		/// </summary>
		/// <value>The keywords.</value>
		public string Keywords
		{
			get
			{
				IList<Term> tags = Tags;
				string[] array = new string[Tags.Count];

				for (int i = 0; i < array.Length; i++)
					array[i] = tags[i];

				return String.Join(", ", array);
			}
		}

		#region Transition Properties

		public bool ShowInList
		{
			get { return true; }
			set { ; }
		}

		public bool IsCommentsEnabled
		{
			get { return CommentStatus == PostCommentStatusType.Open; }
			set { CommentStatus = value ? PostCommentStatusType.Open : PostCommentStatusType.Closed; }
		}

		public bool IsPublished
		{
			get { return Status == PostStatusType.Published; }
			set { Status = value ? PostStatusType.Published : PostStatusType.Pending; }
		}

		public string Description
		{
			get { return Excerpt; }
			set { Excerpt = value; }
		}

		public Uri RelativeLink
		{
			get { return PermaLink; }
		}

		public Uri AbsoluteLink
		{
			get { return PermaLink; }
		}

		#endregion

		#endregion

		#region IPostable Members

		/// <summary>
		/// Gets the author.
		/// </summary>
		/// <value>The author.</value>
		public string Author
		{
			get { return _author; }
			set
			{
				if (_author != value)
					OnPropertyChanged("Author");

				_author = value;
			}
		}

		/// <summary>
		/// Gets the relative link.
		/// </summary>
		/// <value>The relative link.</value>
		public Uri PermaLink
		{
			get
			{
				if (_permaLink == null)
					_permaLink = new Uri(Utils.FormatPermaLink(Slug, Author, DateCreated), UriKind.Relative);

				return _permaLink;
			}
			set
			{
				if (_permaLink != value)
					OnPropertyChanged("PermaLink");

				_permaLink = value;
			}
		}

		/// <summary>
		/// Gets the date published.
		/// </summary>
		/// <value>The date published.</value>
		public DateTime? DatePublished
		{
			get
			{
				if (_datePublished == DateTime.MinValue)
					return _datePublished;

				return _datePublished;
			}
			set
			{
				if (_datePublished != value)
					OnPropertyChanged("DatePublished");

				_datePublished = value;
			}
		}


		/// <summary>
		/// Gets the title of the object
		/// </summary>
		/// <value></value>
		public string Title
		{
			get { return _title; }
			set
			{
				if (_title != value)
					OnPropertyChanged("Title");

				_title = value.Trim();
			}
		}

		/// <summary>
		/// Gets the description.
		/// </summary>
		/// <value>The description.</value>
		public string Excerpt
		{
			get { return _description; }
			set
			{
				if (_description != value)
					OnPropertyChanged("Description");

				_description = value;
			}
		}

		/// <summary>
		/// Gets the content.
		/// </summary>
		/// <value>The content.</value>
		public string Content
		{
			get { return _content; }
			set
			{
				if (_content != value)
					OnPropertyChanged("Content");

				_content = value;
			}
		}

		/// <summary>
		/// Gets the categories.
		/// </summary>
		/// <value>The categories.</value>
		public IList<Term> Categories
		{
			get
			{
				return Terms.FindAll(
					delegate(Term obj)
					{
						return obj.Type == TermType.Category;
					}).AsReadOnly();
			}
		}

		/// <summary>
		/// Gets the tags.
		/// </summary>
		/// <value>The tags.</value>
		public IList<Term> Tags
		{
			get
			{
				return Terms.FindAll(
					delegate(Term obj)
					{
						return obj.Type == TermType.Tag;
					}).AsReadOnly();
			}
		}

		/// <summary>
		/// Gets the parent.
		/// </summary>
		/// <value>The parent.</value>
		public IPostable Parent
		{
			get { return _parent; }
			set
			{
				if (_parent != value)
					OnPropertyChanged("Parent");

				_parent = value;
			}
		}

		/// <summary>
		/// Gets the type of the post.
		/// </summary>
		/// <value>The type of the post.</value>
		public virtual PostType Type
		{
			get { return _type; }
			set
			{
				if (_type != value)
					OnPropertyChanged("Type");

				_type = value;
			}
		}

		/// <summary>
		/// Gets the type of the status.
		/// </summary>
		/// <value>The type of the status.</value>
		public PostStatusType Status
		{
			get { return _status; }
			set
			{
				if (_status != value)
					OnPropertyChanged("Status");

				_status = value;
			}
		}

		/// <summary>
		/// Gets the type of the comment.
		/// </summary>
		/// <value>The type of the comment.</value>
		public PostCommentStatusType CommentStatus
		{
			get { return _commentStatus; }
			set
			{
				if (_commentStatus != value)
					OnPropertyChanged("CommentStatus");

				_commentStatus = value;
			}
		}

		/// <summary>
		/// Gets the type of the ping.
		/// </summary>
		/// <value>The type of the ping.</value>
		public PostPingStatusType PingStatus
		{
			get { return _pingStatus; }
			set
			{
				if (_pingStatus != value)
					OnPropertyChanged("PingStatus");

				_pingStatus = value;
			}
		}

		/// <summary>
		/// Gets the type of the MIME.
		/// </summary>
		/// <value>The type of the MIME.</value>
		public string MimeType
		{
			get { return _mimeType; }
			set
			{
				if (_mimeType != value)
					OnPropertyChanged("MimeType");

				_mimeType = value.Trim();
			}
		}

		/// <summary>
		/// Gets the slug.
		/// </summary>
		/// <value>The slug.</value>
		public string Slug
		{
			get
			{
				if (String.IsNullOrEmpty(_slug) && !String.IsNullOrEmpty(Title))
					_slug = Utils.RemoveIllegalCharacters(Title);

				return _slug;
			}
			set
			{
				value = Utils.RemoveIllegalCharacters(value);

				if (_slug != value)
					OnPropertyChanged("Slug");

				_slug = value.Trim();
			}
		}

		/// <summary>
		/// Gets the meta.
		/// </summary>
		/// <value>The meta.</value>
		public IDictionary<string, string> Meta
		{
			get { return _meta; }
		}

		public StateCollection<Term> Terms
		{
			get { return _terms; }
		}

		void IPostable.FillTerms(IList<Term> terms)
		{
			_terms.AddRange(terms);
		}

		void IPostable.FillMeta(IDictionary<string, string> meta)
		{
			_meta = meta;
		}

		#endregion

		#region IPublishable Members

		/// <summary>
		/// Gets a value indicating whether this instance is visible.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is visible; otherwise, <c>false</c>.
		/// </value>
		public bool IsVisible
		{
			get { return Status == PostStatusType.Published; }
		}

		#endregion
	}
}